home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-08-20 | 17.3 KB | 331 lines | [TEXT/pdos] |
- Rez 103
- by: Tim Swihart
-
- This time around, we'll look at Parts 2.5 and 3 of the resource tutorial.
- We're taking on two parts instead of the traditional one like Rez 101 and
- Rez 102 covered since both Part 2.5 and Part 3 are fairly short.
-
-
- Part 2.5
- The quick summary of Part 2.5 is that it's simply Part 2 with an event loop
- added. If you're totally lost as to what an event loop is then you're going
- to have a VERY hard time writing desktop applications and really should get
- your hands on "Programmer's Introduction to the Apple IIGS" from Addison-
- Wesley (ISBN #: 0-201-17745-5). That book explains in full detail what is
- required to create a desktop application for the IIGS. Among the many
- concepts it explains are event loops. To refresh those of you who are
- vaguely familiar with event loops, that's where the action is in a desktop
- application!
-
- Unlike the apps of many older CPU's, desktop applications allow the user to
- be in control instead of the computer. What's the difference? All menu
- items are always handy instead of having to go into a menu mode and from
- there navigate through several submenus to get things done. Desktop
- applications therefore can't make a lot of assumptions that text-screen apps
- generally do.
-
- To cope with this difference, the application spends its spare time in an
- event loop. That's just a small loop of code that waits for things to happen
- (such as mouse clicks, key presses, etc) and executes various pieces of code
- to cope with each of the different types of events.
-
- To keep things simple, Part 2.5 the event loop I added only handles three
- types of events, They are: wInGoAway (meaning that the user clicked in
- the close box of an open window), wInSpecial, and wInMenuBar (the user
- clicked the mouse on the menu bar - we need to open the menus for the user
- and let them pick something). All other events are either ignored or handled
- for us by TaskMaster.
-
-
- By Who?
- TaskMaster is an extension to the Window Manager in the IIGS Toolbox that
- ALL desktop computers should have (but they don't!). TaskMaster will
- handle all of the "generic" events that it can (windows being moved,
- windows being zoomed, scroll bars being clicked, update events, etc.).
- Having TaskMaster around makes writing desktop applications a LOT easier.
-
- TaskMaster is the big reason that my event loop only cares about a couple of
- events and yet still fully supports NDA's, updates the window's contents
- correctly, can cut/copy/paste, etc. Without TaskMaster, you would have to
- write the code to do all that yourself. No doubt you'd have a bug or two or
- would simply not have some of the functionality that your users expect. So,
- don't call "GetNextEvent" to find out what your app needs to do - call
- "TaskMaster" instead and you'll have a MUCH better app as a result!
-
- More details on TaskMaster can be found in "Programmer's Introduction to
- the IIGS", and Volumes 2 and 3 of the IIGS Toolbox Reference manual. All of
- these books should be in your library if you're serious about writing desktop
- software for the IIGS. You should also read them - just having them in your
- library doesn't do much good if you never read them... <grin>
-
-
- How Did You Do It?
- In order to add the event loop to the application, I had to create several new
- procedures. The first one is named "do_main_event" and is called just after
- we start up the tools. This is where the application's "heart" is - every
- time the app finishes handling an event, it comes back to the event loop to
- see if there are other events waiting to be handled.
-
- Inside of "do_main_event", we call TaskMaster to find out if there are any
- events waiting. TaskMaster requires two things as input - a mask that tells
- it which events you want to know about (each bit controls a different event)
- and an event record to put the detailed information about the event into.
- TaskMaster also returns an event code. We use the event code as part of a
- switch statement ("case" statement if you're using Pascal) to determine what
- to do with the task code. We only get task codes for things that TaskMaster
- couldn't handle.
-
- If a user clicks in the close box of a window, we don't really care - this app
- has no windows of its own yet, and TaskMaster will close NDA windows for
- us.
-
- We really only need to watch for the user clicking in the menu bar. If he
- does, we call "do_menu_events". This is one of the new procedures added
- since Part 2. "do_menu_events" figures out which menu item the user
- selected. We use another switch (case for Pascal folks) statement to handle
- all of the different menu items. Since we're smart about how we program
- and used constants to represent each menu item, it's very easy to see how
- the switch statement for the menu items works - it simply transfers control
- to a different subroutine for each menu items.
-
- Most of the menu items actually result in nothing happening. The only
- "active" menu items in Part 2.5 are the About box (do_show_about gets
- called) and Quit (do_quit_app gets called). Part 3 adds a window to the app
- and Part 3.2 adds an AlertWindow for the About box. For now, selecting the
- About box menu item will result in SysBeep being called. When you're
- creating an application for the first time, it's worth spending some extra time
- being certain that your menu items all work correctly (to catch tiny errors in
- your source). The easiest way to do this is to have each items bring up a
- dialog box saying what item you just picked. It only takes a couple lines of
- code and can catch some pretty silly errors that would slip by otherwise.
-
- The call to TaskMaster is done inside a loop (hence the origin of the name
- "event loop") and we stay in that loop until the user picks "Quit" from the
- menu. Picking "Quit" causes the procedure "do_quit_app" to be called.
- Remember, "do_quit_app" is several levels deep inside the app. We can't
- just quit from here - the stack would be messed up, the tools have to be
- shutdown, etc. We need a graceful way to tell the event loop to quit running
- so control will be returned to the main application where we've already seen
- (in the earlier parts) that things are shutdown nicely.
-
-
- Remember Your Booleans!
- Think hard now - remember the boolean from Part 1 that we set during the
- "do_init_rom" routine? Remember that it was used to control whether or not
- the app should be run? We're going to use it again! (in case you don't
- remember, I'm referring to "gPunt") If you look closely at the start of the
- event loop ("while(!gPunt)") you'll see that as long as gPunt is set to False,
- the event loop will keep running and the app won't shut down.
-
- So, all we really need to do in "do_quit_app" is change the value of gPunt to
- indicate that it's time to shut things down. That causes the event loop to
- terminate and returns control to the main app - letting the tools be shut
- down and then the app itself is done.
-
-
- Why "do_quit_app"?
- Seems a little wasteful to have a procedure that does nothing more than set
- a boolean! Why not just set it on the line where we called "do_quit_app"
- instead? Simple - what happens in a complete app if you've made changes
- to the current document and you pick "Quit" from the menu? The app
- prompts you to save the changes! Where would that come from? Why not
- from a routine like "do_quit_app" that's already set up to handle
- EVERYTHING that's needed to prepare the app for being shut down.
-
- What if the user is prompted for saving their changes and picks "cancel"?
- How would "do_quit_app" handle that? Simple - just leave the boolean
- "gPunt" set to False and the event loop would NOT terminate. Everything
- would just keep running as though the user never picked "Quit" at all. Nice,
- eh?
-
- Structuring your program carefully results in an elegant design and an easy
- to extend application. If we'd just set the boolean right there in the switch
- statement, then we'd have to squeeze in a lot of extra code later when the
- app needed to handle other things before quitting. That would destroy the
- readability of our source code! The easier it is to read your source code, the
- easier it is to debug it!
-
-
- Wrap-up 2.5:
- So, Part 2.5 is pretty simple. Yet, adding the event loop turned our app into
- one that supports NDA's, and "feels" like a desktop application. Prior to Part
- 2.5 the app just started up and shut down - never giving you a chance to do
- anything other than watch. TaskMaster handles all of the things necessary
- to make NDA's work for us (so, NDA support is essentially free) and we
- really only needed to implement ONE menu item at this stage (the "Quit"
- item). Nothing new in the way of resources was added to go from Part 2 to
- Part 2.5. Part 2.5 was named that because it's about halfway between Part 2
- and Part 3.
-
-
- Part 3:
- This part adds a window with a couple of controls in it to the application.
- Using the controls in the window is very simple since we're using
- TaskMaster. Someday, when you're bored and have a LOT of spare time, try
- implement just up to Part 3 WITHOUT using TaskMaster. It's possible, but
- it's a LOT of effort!
-
- The window is stored as a resource (naturally) and the controls in are also
- resources. We could make the controls automatically be installed in the
- window when it's open, but I did it in a separate step for two reasons. The
- first one is to show you how to do it (using NewControl2) and the second
- reason is to avoid a bug in the System Disk 5.0.2 Window Manager that's
- documented in IIGS Tech Note #82 (the tech notes are available online, so I
- won't repeat it here).
-
- To make the window show up, we have to add a couple of new routines to
- our application. The first one is "do_make_window" and it's called from
- within "main" so that we have a window in our application BEFORE the event
- loop gets called. We create the actual window by making ONE tool call ->
- "NewWindow2". NewWindow2 needs quite a bit of information in order to
- do its stuff. First off, it needs to if you want to use a custom title for
- the window (we want to use the one in the resource, so put a NIL here). Next,
- NewWindow2 needs to know if it should use a custom refCon or not - again,
- we want the default, so put a NIL here.
-
- TaskMaster will call our window update routine IF we put a pointer to that
- routine in the third field of the NewWindow2 call. Always do this if at all
- possible - it will make your windows behave much nicer than if you try to
- manually manage all update events that come along.
-
- The fourth item to pass NewWindow2 is a NIL since we do NOT want to use a
- custom def proc for our window. If you're not sure what a def proc is, that's
- what draws the window itself (the frame, title, etc). If you wanted to have a
- round window, you would have to write a custom def proc and set this field
- to point to the def proc.
-
- Fifth, NewWindow2 needs to know what kind of reference it's going to get in
- the sixth field. References were described in Rez 102, so go back and re-
- read about them if you forgot what a reference is! Since we're using
- resources, we put the resource ID of our window template in the sixth field.
-
- The final field of the NewWindow2 call tells the Window Manager what
- template it should use to create the window. There are two different
- standard resource templates that can be used to create windows from
- resources. We're using the first one. If you want to explore the other one,
- go right ahead - it's documented in Apple IIGS Toolbox Reference manual,
- volume 3.
-
- When NewWindow2 is done, it will return a pointer to the newly created
- GrafPort. We need that point for the next couple of tool calls, but otherwise
- we can toss it out (i.e.: it doesn't have to be kept in a global variable).
- If we need it back, we can get it by using tool calls such as FrontWindow,
- GetNextWindow, GetFirstWindow, etc.
-
-
- Watch Out For The Bug!
- The bug I mentioned earlier is simply that the GrafPort isn't set right in
- certain cases before controls are installed in a window. To avoid it, we
- simply set the current port (using the tool call "SetPort") to the newly
- created window's GrafPort (which NewWindow2 returned a couple of
- paragraphs ago).
-
- Now that the port is set right, we can add our controls to the window's
- contents by calling "NewControl2". NewControl2 loads controls from the
- resource fork and attaches them to a given window. If you want to put only
- one or tons of controls in a window, NewControl2 is for you!
-
-
- Elaborate Please...
- NewControl2 wants the GrafPort of the window it's supposed to put controls
- in, it wants to know what kind of reference it's going to get, and it wants
- the actual reference (resource ID of the control list in our case).
-
- NewControl2 looks for a list of controls and installs all of them with ONE tool
- call. To keep our app simple, we're adding only two controls - a push button
- (pretty common) and a TextEdit field (to show the power of TaskMaster).
-
-
- TaskMaster Again?
- Yep - TaskMaster makes it VERY easy to track controls in a window. Without
- TaskMaster, you would get a mouseDown event (telling you the mouse was
- clicked). You'd have to find out WHERE it was clicked (in the menu bar? in
- the window? etc). If it was clicked in the window's content area, then you'd
- have to call FindControl (to see if it was clicked over a control such as a
- push button, radio button, etc). If it was clicked over a control, you have
- to call TrackControl to be sure the mouse is over the same control when it's
- released! Then you have to do whatever action that controls calls for. UGH!!!
-
- With TaskMaster, you're told AFTER the mouse comes up over the control
- that you need to do whatever action that control calls for. No more figuring
- out where the mouse went down at! No more FindControl/TrackControl!
- YAY!
-
- In the case of the TextEdit field, Cut/Copy/Paste/Clear/Undo are all done by
- TaskMaster! Look closely at the do_menu_events routine and you'll see that
- our application does NOTHING in response to the user picking Undo, Cut,
- Copy, Paste, or Clear from the menus! Sure saves a lot of code, eh?
-
- Like I said, TaskMaster is your friend - learn to let it do what it does best
- and spend your effort on the stuff that's unique to the application you're
- writing.
-
-
- Drawing The Contents
- TaskMaster will call our window content drawing routine for us as soon as
- there's some spare time. This spare time is known as "null events" and it
- occurs when the computer gets bored (because it's waiting on the user to do
- something that generates an event). Since all of our contents this time
- around are controls, we can draw then with ONE tool call!
-
- Before we call that one tool, we have to know what GrafPort to draw them in,
- so we call GetPort to find that out. See, tool call names are pretty easy to
- anticipate - that makes desktop programming MUCH easier!
-
- The one call that draws it all is "DrawControls". We tell it what GrafPort to
- draw in and it figures out what all the controls are, loads them from the
- resource fork if needed, and draws them! Nice, simple, compact, complete,
- easy! The way it should be.
-
- Remember - the application itself doesn't call the content drawing procedure,
- TaskMaster does! So if you're digging through the source code and can't
- figure out who's calling these routine, so you want to throw it out - think
- again!
-
-
- Play With It!
- Run the application that results from compiling the source code to rTutor
- Part 3. Try dragging the window around - naturally it works, but there's no
- code in our application to do that. TaskMaster does the dragging for us
- (without TaskMaster, we'd have to do it ourselves - UGH!) Try cutting and
- pasting from the TextEdit field in the window. It works like you'd expect -
- thanks to TaskMaster. Try clicking on the push button - works like it should
- thanks to TaskMaster.
-
- If you haven't caught on by now (I know, some of the hints were pretty
- subtle), then I'll be more explicit -> Use TaskMaster whenever possible!
- Your application will "feel" better to the user! Your application will be able
- to do more of the "little things" that users expect. TaskMaster is part of the
- System, so why waste disk space duplicating its effort within your
- application? Just use TaskMaster and all will be right with the world...
-
-
- Wrap Up Time:
- OK, enough reading - get to it! Spend some time studying the source code.
- Look up each of the tool calls that you're not familiar with (you'll need Apple
- IIGS Toolbox Reference Volume 3 for the calls that deal with resources). If
- you're hopelessly lost, post a plea for help on either GEnie or America
- Online's developer forums. On GEnie, post in A2Pro. On America Online, post
- in ADV. If you're on another network, find a way to get to where I am or
- post there - I'm not the only one who can field questions on resources. Lots
- of folks can deal with it just fine (they're just too lazy to write detailed
- docs like these). <grin>
-
-
- If you don't have the following manuals yet, go get them! They're all
- Addison-Wesley books, so your local bookstore (B. Dalton's, Walden's, etc)
- should either have them or can order them if you bring them the ISBN
- number:
-
-
- Book Title ISBN Number
- -------------------------------------------------------------
- Programmer's Introduction to the Apple IIGS 0-201-17745-5
- Apple IIGS Toolbox Reference, Volume 3 0-201-55019-9
- Apple IIGS Toolbox Reference, Volume 2 0-201-17747-1
- Apple IIGS Toolbox Reference, Volume 1 0-201-17746-3
-
-
- Enjoy!
- Tim S.
-